package com.gopay.solution.service.decision;

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import com.gopay.common.domain.solution.SolutionRequest;
import com.gopay.common.domain.solution.TranBasicReq;
import com.gopay.solution.domain.po.*;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.gopay.common.constants.txncd.IntTxnCd;
import com.gopay.common.domain.poundage.PoundageReq;
import com.gopay.common.domain.solution.GpRiskFundReq;
import com.gopay.common.domain.solution.TranLimitReq;
import com.gopay.common.util.DateUtils;
import com.gopay.common.constants.solution.SolutionTypeConstant;
import com.gopay.common.util.JsonUtils;
import com.gopay.common.util.xml.tools.XmlToolsService;
import com.gopay.solution.dao.SsAcctSolutionRelDAO;
import com.gopay.solution.dao.SsApSolutionRelDAO;
import com.gopay.solution.dao.SsCapSolutionRelDAO;
import com.gopay.solution.dao.SsPlSolutionRelDAO;
import com.gopay.solution.domain.bo.SolutionRuleExecutor;
import com.gopay.solution.domain.vo.AcctTreeNode;
import com.gopay.solution.domain.vo.Event;
import com.gopay.solution.domain.vo.Solution;
import com.gopay.solution.manager.AcctTreeModelManager;
import com.gopay.solution.manager.SolutionManager;

/**
 * 决策工厂，负责对传入账户的方案配置做出决策
 * 
 * @DecisionFactory.java
 * @author MA HUAN
 * @2013-3-26 下午4:38:00 www.gopay.com.cn Inc.All rights reserved.
 */
@Service("decisionFactory")
public class DecisionFactory {
	
	 private Logger logger = LoggerFactory.getLogger(DecisionFactory.class);

    @Resource(name = "solutionManagerMap")
    private Map<String, SolutionManager> solutionManagerMap;

    @Resource(name = "solutionRuleExecutorMap")
    private Map<String, SolutionRuleExecutor> solutionRuleExecutorMap;

    @Resource(name = "acctTreeModelManager")
    private AcctTreeModelManager acctTreeModelManager;

    @Resource(name = "ssAcctSolutionRelDAO")
    private SsAcctSolutionRelDAO ssAcctSolutionRelDAO;

    @Resource(name = "ssApSolutionRelDAO")
    private SsApSolutionRelDAO ssApSolutionRelDAO;

    @Resource(name = "ssPlSolutionRelDAO")
    private SsPlSolutionRelDAO ssPlSolutionRelDAO;
    
    @Resource(name = "ssCapSolutionRelDAO")
    private SsCapSolutionRelDAO ssCapSolutionRelDAO;
    
/*    @Resource(name = "xmlToolsService")
    private XmlToolsService xmlToolsService;*/

    /**
     * 决策防范
     * 
     * @param acctId
     *            账户ID
     * @param solutionType
     *            请求方案类型
     * @param input
     *            方案输入数据
     * @return 决策信息
     */
    public Decision makeDecision(String acctId, String solutionType, Serializable input) {

        boolean isMade = false;
        String failedReason = null;

        Solution solu = null;
        SolutionRuleExecutor executor = null;
        Event event = null;

        try {

            if (SolutionTypeConstant.CUST_TLCS.equals(solutionType)) {
                solu = lookupSolution(acctId, input, solutionType);
            } else if (SolutionTypeConstant.GP_RISKFUND.equals(solutionType)){
            	 solu = lookupSolution(input,acctId, solutionType);
            }else {
                solu = lookupSolution(acctId, solutionType);
            }
             
            //add by zhuliang at 20160316 begin 修改央行检测数字证书手续费为零的问题
            //个性化方案中手续费的交易码不存在,查询通用方案中的手续费交易码
            boolean exist = isIntTxnCodeExist(solutionType, solu, input);
           // logger.info("个性化返回结果exist ===" + exist);
            if(!exist&&(solutionType.equals(SolutionTypeConstant.TRANS_FEE) ||
                    solutionType.equals(SolutionTypeConstant.TRANS_BASIC)
//        		 	|| solutionType.equals(SolutionTypeConstant.TRANS_UIS) 
//    		 		|| solutionType.equals(SolutionTypeConstant.TRANS_PAYCHAN)
            )){
              //  logger.info("个性化返回结果exist ===" + exist + ",获取通用方案");
            	solu = againLookupSolution(acctId,solutionType);
            }
            //add by zhuliang at 20160316 end 
            
            if (solu != null) {
                executor = lookupExecutor(solutionType);
//                logger.error("makeDecision executor: "+JsonUtils.toJson(executor).toString());
                if (executor != null) {
                    event = makeEvent(input);
                    isMade = true;
                } else {
                    failedReason = "";
                }

            } else {
                failedReason = "";
            }
        } catch (Exception e) {
        	logger.error("DecisionFactory.makeDecision Exception", e);
            failedReason = "";
        }
        return new Decision(isMade, failedReason, acctId, solutionType, event, solu, executor);

    }

    private Event makeEvent(Serializable input) {

        Event e = new Event();

        e.setSeq("e_" + System.currentTimeMillis());
        e.setTimestamp(DateUtils.getSystemDate());
        e.setDetail(input);

        return e;
    }

    private SolutionRuleExecutor lookupExecutor(String solutionType) {
        SolutionRuleExecutor exec = solutionRuleExecutorMap.get(solutionType);

        if (exec != null) {
            return exec;
        }

        throw new RuntimeException("Cannot find SolutionRuleExecutor with Parameter[solutionType = " + solutionType
                + "]");
    }

    private Solution lookupSolution(String acctId, Serializable input, String solutionType) {

        long solutionSeq = getSolution(input, solutionType);
        SolutionManager manager = solutionManagerMap.get(solutionType);
        if (manager != null) {
            return manager.load(solutionSeq);
        }

        throw new RuntimeException("Cannot find SolutionManager with Parameter [solutionType = " + solutionType + "]");
    }
    private Solution lookupSolution(Serializable input,String acctId, String solutionType) {
    	
    	long solutionSeq = getSolution(input,acctId, solutionType);
    	SolutionManager manager = solutionManagerMap.get(solutionType);
    	if (manager != null) {
    		return manager.load(solutionSeq);
    	}
    	
    	throw new RuntimeException("Cannot find SolutionManager with Parameter [solutionType = " + solutionType + "]");
    }

    private Solution lookupSolution(String acctId, String solutionType) {

        long solutionSeq = getSolution(acctId, solutionType);

        SolutionManager manager = solutionManagerMap.get(solutionType);
        if (manager != null) {
            return manager.load(solutionSeq);
        }

        throw new RuntimeException("Cannot find SolutionManager with Parameter [solutionType = " + solutionType + "]");
    }

    /*
     * 获取账户方案ID
     * 
     * @param input
     * 
     * @param solutionType
     * 
     * @return
     */
    private long getSolution(Serializable input, String solutionType) {
        TranLimitReq req = (TranLimitReq)input;
        String custId = req.getCustId();
        String prodCode = req.getAcctProd().toString();
        String date = req.getTranDate();
        Date date_ = null;
        if(StringUtils.isBlank(date)){
        	date_ = DateUtils.getSystemDate();
        }else{
        	date_ = DateUtils.strToDate(date, "yyyyMMdd");
        }
        SsCapSolutionRel rel = ssCapSolutionRelDAO.getByCustIdAndPdAndSolType(custId, prodCode, solutionType);
        if(null != rel && isSolutionAvaliable(rel.getSolutionId(), solutionType, date_)){
            return rel.getSolutionId();
        }
        
        SsApSolutionRel apRel = ssApSolutionRelDAO.getByApIdAndSoluType(prodCode, solutionType);
        if(null != apRel && isSolutionAvaliable(apRel.getSolutionId(), solutionType, date_)){
            return apRel.getSolutionId();
        }
        throw new IllegalStateException("No Solution Set for cust or its prodCode, even for its Prodline");
    }
    
    /**
     * 获取账户方案ID
     * @param input
     * @param acctId
     * @param solutionType
     * @return
     * 服务于风险金账户计算，原因：风险金计算出传入的参数是平台账户，而查询是风险金方案
     */
    private long getSolution(Serializable input, String acctId,String solutionType) {
    	GpRiskFundReq req = (GpRiskFundReq)input;
        String prodCode = req.getAcctProd();
        
        SsAcctSolutionRel acctRel = ssAcctSolutionRelDAO.getByAcctIdAndSoluType(acctId, solutionType);
        if (acctRel != null && isSolutionAvaliable(acctRel.getSolutionId(), solutionType)) {
            return acctRel.getSolutionId();
        }
        
        SsApSolutionRel apRel = ssApSolutionRelDAO.getByApIdAndSoluType(prodCode, solutionType);
        if(null != apRel && isSolutionAvaliable(apRel.getSolutionId(), solutionType)){
            return apRel.getSolutionId();
        }
        throw new IllegalStateException("No Solution Set for cust or its prodCode, even for its Prodline");
    }
    	

    /*
     * 获取账户方案ID
     * 
     * @param acctId
     * 
     * @param solutionType
     * 
     * @return
     */
    private long getSolution(String acctId, String solutionType) {

        // 1. 从账户节点获取方案ID
        SsAcctSolutionRel acctRel = ssAcctSolutionRelDAO.getByAcctIdAndSoluType(acctId, solutionType);
        if (acctRel != null && isSolutionAvaliable(acctRel.getSolutionId(), solutionType)) {
            return acctRel.getSolutionId();
        }

        // 如果账户节点没有指定账户方案，则首先加载账户的产品模型树，然后自下而上retrieve方案
        AcctTreeNode acct = acctTreeModelManager.retrieveFromAcct(acctId);
        // 如果账户为空，或者账户所属产品为空，或账户所属产品的产品ID为空
        if (acct == null || acct.getType() != AcctTreeNode.TYPE.ACCT || acct.getFather() == null
                || acct.getFather().getType() != AcctTreeNode.TYPE.PROD
                || StringUtils.isEmpty(acct.getFather().getData())) {
            throw new IllegalStateException("The Acct Belongs to NONE Account Product!, acctId = " + acctId);
        }

        // 2. 从账户产品获取方案ID
        AcctTreeNode prod = acct.getFather();
        SsApSolutionRel apRel = ssApSolutionRelDAO.getByApIdAndSoluType(prod.getData(), solutionType);
        if (apRel != null && isSolutionAvaliable(apRel.getSolutionId(), solutionType)) {
            return apRel.getSolutionId();
        }

        // 如果账户产品所属产品线为空，或者所属产品线ID为空
        if (prod.getFather() == null || prod.getFather().getType() != AcctTreeNode.TYPE.LINE
                || StringUtils.isEmpty(prod.getFather().getData())) {
            throw new IllegalStateException("The Acct Belongs to NONE Product Line!, acctId = " + acctId
                    + ", acctProdCode = " + prod.getData());
        }

        // 3. 从产品线获取方案ID
        AcctTreeNode line = prod.getFather();
        SsPlSolutionRel plRel = ssPlSolutionRelDAO.getByPlIdAndSoluType(line.getData(), solutionType);
        if (plRel != null && isSolutionAvaliable(plRel.getSolutionId(), solutionType)) {
            return plRel.getSolutionId();
        }

        throw new IllegalStateException("No Solution Set for Acct or its Product, even for its Prodline");
    }
    
    /*
     *判断方案是否可用
     * @param solutionSeq
     * @param solutionType
     * @return
     */
    private boolean isSolutionAvaliable(long solutionSeq, String solutionType, Date current) {

        SolutionManager manager = solutionManagerMap.get(solutionType);
        if (manager != null) {
            Solution solution = manager.load(solutionSeq);
            if(null != solution.getMeta()) {
            	//如果开始时间或结束时间为空，则不做有效性判断，默认为可用
            	if(StringUtils.isEmpty(solution.getMeta().getStartDate()) 
            			&& StringUtils.isEmpty(solution.getMeta().getEndDate())) {
		        	return true;
            	}
            	
            	//判断当前系统时间是否在方案有效期内
            	
            	String start = solution.getMeta().getStartDate() + "000000";
            	String end = solution.getMeta().getEndDate() + "235959";
            	
	        	Date startDate = DateUtils.strToDate(start);
	        	Date endDate = DateUtils.strToDate(end);
	        	if(current.before(startDate) || current.after(endDate)) {
	        		return false;
	        	}
	        	
	        	return true;
            }
            
            throw new RuntimeException("Cannot find SolutionMeta with Parameter [solutionType = " + solutionType + ",solutionSeq = " + solutionSeq + " ]");
        }

        throw new RuntimeException("Cannot find SolutionManager with Parameter [solutionType = " + solutionType + "]");
    }
    
    /*
     *判断方案是否可用
     * @param solutionSeq
     * @param solutionType
     * @return
     */
    private boolean isSolutionAvaliable(long solutionSeq, String solutionType) {
    	Date current = DateUtils.getSystemDate();
    	return isSolutionAvaliable(solutionSeq, solutionType, current);
    }
    
    /*
     * 判断交易码是否存在
     * @param solutionType
     * @param solu
     * @param input
     * @return
     */
    private boolean isIntTxnCodeExist(String solutionType,Solution solu,Serializable input){
    	boolean feeTxnExist = false;

        //logger.info("solutionType==" + solutionType);

    	if(SolutionTypeConstant.TRANS_FEE.equals(solutionType)
        		&& null != solu.getRuleDtls()){
            PoundageReq req = (PoundageReq) input;
            IntTxnCd intTxnCd = req.getIntTxnCd();
            List<SolutionRuleDtl> rules = solu.getRuleDtls();
            for (SolutionRuleDtl rule : rules) {
            	SsTransFeeDtl  fee = (SsTransFeeDtl) rule;
                //交易码存在
                //个性化手续费方案是否存在的筛选条件除了交易码，必须加上付款渠道和状态  edit by fanghw 20170122
                if (StringUtils.equals(fee.getGopayIntTxnCd(), intTxnCd.value)
                        &&fee.getPayChannel().equals(req.getPayChannel())&&
                        "00".equals(fee.getDtlStatus())) {
                    feeTxnExist=true;
                    break;
                }
            }
        } else if(SolutionTypeConstant.TRANS_BASIC.equals(solutionType)
                && null != solu.getRuleDtls()){
            TranBasicReq req = (TranBasicReq) input;
            IntTxnCd intTxnCd = req.getIntTxnCd();
            List<SolutionRuleDtl> rules = solu.getRuleDtls();
            for (SolutionRuleDtl rule : rules) {
                SsTransBasicDtl  basic = (SsTransBasicDtl) rule;
                //交易码存在
                if (StringUtils.equals(basic.getGopayIntTxnCd(), intTxnCd.value)) {
                    feeTxnExist=true;
                    break;
                }
            }
        }
    	/*----start add by zhoupeng 20180112 处理存量老用户个性化方案数据不全问题*/
//      else if(SolutionTypeConstant.TRANS_UIS.equals(solutionType)
//              && null != solu.getRuleDtls()){
//          List<SolutionRuleDtl> rules = solu.getRuleDtls();
//          for (SolutionRuleDtl rule : rules) {
//          	SsUIConfigDtl  uiConfig = (SsUIConfigDtl) rule;
//              //交易码存在
//          	if (null != uiConfig) {
//                  ViewSolutionConfig config = (ViewSolutionConfig) xmlToolsService.xml2j(
//                          XmlMsgType.VIEW_CASHIER_UI_PAYCHANNEL_TYPE, uiConfig.getConfigExp());
//                  if (null != config) {
//                      List<View> views = config.getViews();
//                      for (int i = 0, size = null == views ? 0 : views.size(); i < size; i++) {
//                      	ViewSolutionTransView view = (ViewSolutionTransView) views.get(i);
//                          if (null != view && view.getIntTxnCd().equals(input.toString())) {
//                          	feeTxnExist=true;
//                              break;
//                          }
//                      }
//                  }
//              }
//          }
//      }
//      else if(SolutionTypeConstant.TRANS_BASIC.equals(solutionType)
//              && null != solu.getRuleDtls()){
//          List<SolutionRuleDtl> rules = solu.getRuleDtls();
//          for (SolutionRuleDtl rule : rules) {
//          	SsTransPayChanDtl  basic = (SsTransPayChanDtl) rule;
//              //交易码存在
//              if (StringUtils.equals(basic.getGopayIntTxnCd(), input.toString())) {
//                  feeTxnExist=true;
//                  break;
//              }
//          }
//      }
  	/*---end add by zhoupeng 20180112 处理存量老用户个性化方案数据不全问题*/
    	return feeTxnExist;
    }
    
    private Solution againLookupSolution(String acctId, String solutionType) {

        long solutionSeq = againGetSolution(acctId, solutionType);

        SolutionManager manager = solutionManagerMap.get(solutionType);
        if (manager != null) {
            return manager.load(solutionSeq);
        }

        throw new RuntimeException("Cannot find SolutionManager with Parameter [solutionType = " + solutionType + "]");
    }
    
    /*
     * 获取账户方案ID
     * 
     * @param acctId
     * 
     * @param solutionType
     *
     * @return
     */
    private long againGetSolution(String acctId, String solutionType) {

        // 1. 从账户节点获取方案ID
//        SsAcctSolutionRel acctRel = ssAcctSolutionRelDAO.getByAcctIdAndSoluType(acctId, solutionType);
//        if (acctRel != null && isSolutionAvaliable(acctRel.getSolutionId(), solutionType)) {
//            return acctRel.getSolutionId();
//        }

        // 如果账户节点没有指定账户方案，则首先加载账户的产品模型树，然后自下而上retrieve方案
        AcctTreeNode acct = acctTreeModelManager.retrieveFromAcct(acctId);
        // 如果账户为空，或者账户所属产品为空，或账户所属产品的产品ID为空
        if (acct == null || acct.getType() != AcctTreeNode.TYPE.ACCT || acct.getFather() == null
                || acct.getFather().getType() != AcctTreeNode.TYPE.PROD
                || StringUtils.isEmpty(acct.getFather().getData())) {
            throw new IllegalStateException("The Acct Belongs to NONE Account Product!, acctId = " + acctId);
        }

        // 2. 从账户产品获取方案ID
        AcctTreeNode prod = acct.getFather();
        SsApSolutionRel apRel = ssApSolutionRelDAO.getByApIdAndSoluType(prod.getData(), solutionType);
        if (apRel != null && isSolutionAvaliable(apRel.getSolutionId(), solutionType)) {
            return apRel.getSolutionId();
        }

        // 如果账户产品所属产品线为空，或者所属产品线ID为空
        if (prod.getFather() == null || prod.getFather().getType() != AcctTreeNode.TYPE.LINE
                || StringUtils.isEmpty(prod.getFather().getData())) {
            throw new IllegalStateException("The Acct Belongs to NONE Product Line!, acctId = " + acctId
                    + ", acctProdCode = " + prod.getData());
        }

        // 3. 从产品线获取方案ID
        AcctTreeNode line = prod.getFather();
        SsPlSolutionRel plRel = ssPlSolutionRelDAO.getByPlIdAndSoluType(line.getData(), solutionType);
        if (plRel != null && isSolutionAvaliable(plRel.getSolutionId(), solutionType)) {
            return plRel.getSolutionId();
        }

        throw new IllegalStateException("No Solution Set for Acct or its Product, even for its Prodline");
    }

           
}
